#include "node.h"
#include "../random.h"
#include <typeinfo>
#include <QRegExp>

Node::Node(double val)
{
    value = val;
    isObserved = true;
    useCandidate = false;    
    isNecessary = true;

    parents  = new QVector<Node * >();
    children = new QVector<Node * >();

    randomNumberGenerator = Random::getGenerator();

    stats = new NodeStats();            
}

Node::Node(QString lbl, double val, double sd, bool isObserved)
{    
    label      = lbl;
    value      = val;
    candSd     = sd;
    this->isObserved = isObserved;
    isNecessary = true;

    parents  = new QVector<Node * >();
    children = new QVector<Node * >();

    useCandidate   = false;
    candidateValue = 0;

    randomNumberGenerator = Random::getGenerator();

    stats = new NodeStats();   
}


Node::~Node()
{    
    delete parents;
    delete children;
}

double Node::getCandSd()
{
    return candSd;
}

QString Node::getLabel()
{
    return label;
}

double Node::getValue()
{
    if(useCandidate)
    {
        return candidateValue;
    }

    return value;
}


QVector<Node * > * Node::getParentNodes()
{
    return parents;
}


QVector<Node * > Node::getExplicitParentNodes()
{
    QVector<Node *> explicitParents;
    QString className;

    foreach(Node * n, *parents)
    {
        className = typeid(*n).name();
        className.replace(QRegExp("^\\d*"), "");

        if(className != "Node")
            explicitParents.append(n);
    }

    return explicitParents;
}


QVector<Node * > * Node::getChildNodes()
{
    return children;
}

bool Node::getIsObservation()
{
    return isObserved;
}

void Node::setObserved(bool val)
{
    isObserved = val;
}

bool Node::inRange(double v)
{
    return true;
}

int Node::addParentNode(Node * node)
{    
    node->addChildNode(this);
    parents->append(node);
    return parents->size() - 1;
}

void Node::addChildNode(Node * node)
{
    children->append(node);
}

double Node::generateSampleFromDistribution()
{
    if(candSd < 0.01)
        return value;

    return gsl_ran_gaussian(randomNumberGenerator, candSd) + value;
}

QVector<double> * Node::generateSamples(int N)
{
    QVector<double> * samples = new QVector<double>();
    for(int i = 0; i < N; i++)
    {
        samples->append(generateSample());
    }
    return samples;
}



double Node::generateSample()
{
    // get random sample from proposal distribution
    candidateValue = generateSampleFromDistribution();

    // throw out negative / zero values immediately
    if(!inRange(candidateValue))
        return value;

    // compute importance ratio
    // f(x*)/f(x_t) = log(f(x*)) - log(f(x_t))
    useCandidate = true;
    double lh_cand = computeCompleteConditional();
    useCandidate = false;
    double lh_last = computeCompleteConditional();

    // generate random uniform value in log space
    double t = gsl_ran_flat(randomNumberGenerator, 0.0, 1.0);
    double p = VALID_LOG(t);

    if(p < lh_cand - lh_last)
    {
        value = candidateValue;
    }
    // else prevSample = prevSample

    return value;
}



double Node::computeCompleteConditional()
{
    double log_result = computeLogLikelihood();

    // for each child
    for(int i = 0; i < children->size(); i++)
    {
        log_result += children->at(i)->computeLogLikelihood();
    }

    return log_result;
}
